#ifndef QUEUE_H
#define QUEUE_H

#include <stdint.h>
#include <stdbool.h>

typedef struct {
    volatile bool locked;  ///< Mutex lock status
} queue_mutex_t;

// Define the queue structure
typedef struct queue_t {
    uint32_t cur_size;        // Current size of the queue
    uint32_t max_size;        // Maximum number of items the queue can hold
    uint32_t max_type_size;   // Maximum size of each item in the queue
    char *pc_head;            // Pointer to the head of the queue buffer
    char *pc_tail;            // Pointer to the tail of the queue buffer
    char *pc_read;            // Pointer to the read position in the queue
    char *pc_write;           // Pointer to the write position in the queue
    queue_mutex_t queue_mutex;// Mutex to handle synchronization
} queue_t;

// Enum for queue status return values
typedef enum {
    QUEUE_SUCCESS = 0,
    QUEUE_NULL_POINTER,
    QUEUE_FULL,
    QUEUE_EMPTY,
} queue_status_t;

// Function prototypes

/**
 * @brief Creates a new queue with specified size and item type size
 * 
 * @param data_size The maximum number of items the queue can hold
 * @param data_type The size of each item in the queue (in bytes)
 * @return queue_t* Pointer to the created queue, or NULL if memory allocation failed
 */
queue_t *queue_create(uint32_t data_size, uint32_t data_type);

/**
 * @brief Destroys the queue and frees allocated memory
 * 
 * @param q Pointer to the queue to destroy
 */
void queue_destory(queue_t **q);

/**
 * @brief Checks if the queue is full
 * 
 * @param q Pointer to the queue
 * @return true if the queue is full, false otherwise
 */
bool queue_is_full(queue_t *q);

/**
 * @brief Checks if the queue is empty
 * 
 * @param q Pointer to the queue
 * @return true if the queue is empty, false otherwise
 */
bool queue_is_empty(queue_t *q);

/**
 * @brief Sends a message to the queue, overwriting the oldest message if full
 * 
 * @param q Pointer to the queue
 * @param message Pointer to the message to send
 * @return queue_status_t QUEUE_SUCCESS if the message was successfully added, 
 *                        QUEUE_NULL_POINTER if the queue pointer is NULL, 
 *                        QUEUE_FULL if the queue is full
 */
queue_status_t queue_send_cover(queue_t *q, const void *message);

/**
 * @brief Sends a message to the queue
 * 
 * @param q Pointer to the queue
 * @param message Pointer to the message to send
 * @return queue_status_t QUEUE_SUCCESS if the message was successfully added, 
 *                        QUEUE_NULL_POINTER if the queue pointer is NULL, 
 *                        QUEUE_FULL if the queue is full
 */
queue_status_t queue_send(queue_t *q, const void *message);

/**
 * @brief Receives a message from the queue
 * 
 * @param q Pointer to the queue
 * @param message Pointer to a variable where the received message will be stored
 * @return queue_status_t Returns QUEUE_SUCCESS if the message was successfully received, 
 *                        QUEUE_NULL_POINTER if the queue pointer is NULL, 
 *                        QUEUE_EMPTY if the queue is empty
 */
queue_status_t queue_receive(queue_t *q, void **message);

/**
 * @brief Receives a message from the queue and copies it to the provided buffer.
 *
 * This function attempts to retrieve a message from the given queue. If the message is successfully 
 * received, it is copied into the buffer provided by the `message` parameter. The size of the copied
 * message is determined by the `max_type_size` field of the queue structure.
 *
 * @param q [in] A pointer to the queue from which the message will be received.
 * @param message [out] A pointer to the buffer where the received message will be copied.
 * 
 * @return queue_status_t The status of the receive operation. It can be one of the following:
 *         - QUEUE_SUCCESS: Message received and copied successfully.
 *         - Other status codes indicating failure or error (e.g., QUEUE_EMPTY, QUEUE_ERROR, etc.).
 */
queue_status_t queue_receive_value(queue_t *q, void *message);

/**
 * @brief Clears the queue (removes all messages)
 * 
 * @param q Pointer to the queue
 */
void queue_clear(queue_t *q);

/**
 * @brief Peeks at the front of the queue without removing the item
 * 
 * @param q Pointer to the queue
 * @param message Pointer to a variable where the peeked message will be stored
 * @return queue_status_t Returns QUEUE_SUCCESS if the message was successfully peeked, 
 *                        QUEUE_NULL_POINTER if the queue pointer is NULL, 
 *                        QUEUE_EMPTY if the queue is empty
 */
queue_status_t queue_peek(queue_t *q, void **message);

/**
 * @brief Peeks at the next message in the queue and copies it to the provided buffer.
 *
 * This function attempts to peek at the next message in the specified queue without removing 
 * it from the queue. If successful, the message is copied into the provided buffer, with 
 * the size determined by the `max_type_size` field of the queue structure. The queue state is 
 * unchanged after this operation, meaning the message remains in the queue.
 *
 * @param q [in] A pointer to the queue from which the message will be peeked.
 * @param message [out] A pointer to the buffer where the peeked message will be copied.
 * 
 * @return queue_status_t The status of the peek operation:
 *         - `QUEUE_SUCCESS` on success, meaning a message was successfully peeked and copied.
 *         - Other status codes may indicate errors such as the queue being empty or other failure conditions.
 */
queue_status_t queue_peek_value(queue_t *q, void *message);
/**
 * @brief Retrieves the current size of the queue
 * 
 * @param q Pointer to the queue
 * @return uint32_t The current number of items in the queue
 */
uint32_t queue_size(queue_t *q);

/**
 * @brief Retrieves the maximum capacity of the queue
 * 
 * @param q Pointer to the queue
 * @return uint32_t The maximum size of the queue
 */
uint32_t queue_get_max_size(queue_t *q);

/**
 * @brief Retrieves the maximum size of each item in the queue
 * 
 * @param q Pointer to the queue
 * @return uint32_t The size of each item in the queue
 */
uint32_t queue_get_item_size(queue_t *q);

/**
 * @brief Checks if the queue is locked
 * 
 * @param q Pointer to the queue
 * @return bool true if the queue is locked, false otherwise
 */
bool queue_is_locked(queue_t *q);

/**
 * @brief Debugging function to print the state of the queue
 * 
 * @param q Pointer to the queue
 */
void queue_print_debug_info(queue_t *q);

/**
 * @brief Prints the current contents of the queue
 * 
 * @param q Pointer to the queue
 */
void queue_print(queue_t *q);

#endif // QUEUE_H
